package jobs

import (
	"cronjob/models"
	"cronjob/notify"
	"fmt"

	"runtime/debug"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"
)

type Job struct {
	id    int
	logId int64  //日志id
	name  string //日志名
	task  *models.Task

	//  duration  超时时间
	// 返回值1 任务输出
	// 返回值2 错误信息
	// 返回值3 出现的错误
	// 返回值4 是否超时
	returnFun func(duration time.Duration) (string, string, error, bool)
	// 任务状态大于0 标识正常执行
	status int
	//同一个任务是否允许并发执行
	Concurrent bool
}

func (j *Job) GetId() int {
	return j.id
}

func (j *Job) Run() {
	//调试信息
	beego.Info(j.task.Id)

	//该任务不允许并发执行 且该任务正在运行
	if j.Concurrent && j.status > 0 {
		beego.Warn(fmt.Sprintf("任务%d上一次执行未结束，本次忽略", j.id))
		return
	}

	// 错误恢复
	defer func() {
		if err := recover(); err != nil {
			beego.Error(err, "\n", string(debug.Stack()))
		}
	}()

	// 修改状态
	j.status++

	defer func() {
		j.status--
	}()

	beego.Debug(fmt.Sprintf("开始执行任务: %d", j.id))

	//设置默认超时时间
	timeout := time.Duration(time.Hour * 24)

	//任务的超时时间大于0，则说明用户设置了超时时间，故修改超时时间的默认值
	if j.task.Timeout > 0 {
		timeout = time.Second * time.Duration(j.task.Timeout)
	}

	//执行开始时间
	start := time.Now()
	//执行命令

	cmdOut, cmdErr, err, isTimeout := j.returnFun(timeout)

	beego.Info(fmt.Sprintf("数据类型%T", cmdOut))

	log := new(models.TaskLog)
	log.TaskId = j.id
	log.Output = cmdOut
	log.Error = cmdErr
	//执行结束时间
	end := time.Now()
	log.ProcessTime = int(end.Sub(start) / time.Millisecond)
	//日志创建时间
	log.CreateTime = end.Unix()

	if isTimeout {
		log.Status = models.TASK_TIMEOUT //执行超时-2
		log.Error = fmt.Sprintf("任务执行超时%d秒\n ----------\n", timeout/time.Second, cmdOut)
	} else if err != nil {
		log.Status = models.TASK_ERROR //执行错误 -1
		log.Error = err.Error() + ":" + cmdErr
	}

	// 任务没有执行成功并且需要通知管理员
	if log.Status < 0 && j.task.IsNotify == 1 {
		if j.task.NotifyUserIds != "0" && j.task.NotifyUserIds != "" {

			// 根据管理员id的email
			adminInfo := AllAdminInfo(j.task.NotifyUserIds)

			toEmail := ""

			//拼接邮箱mail
			for _, v := range adminInfo {
				if v.Email != "0" && v.Email != "" {
					toEmail = toEmail + ";"
				}
			}
			//去除拼接最后的:
			toEmail = strings.TrimRight(toEmail, ";")
			/*
				TASK_SUCCESS = 0
				TASK_ERROR   = -1
				TASK_TIMEOUT = -2
			*/
			//创建切片，用于存储任务执行之后可能的状态
			TextStatus := []string{
				"<font color='red'>超时</font>",
				"<font color='red'>错误</font>",
				"<font color='green'>正常</font>",
			}
			status := log.Status + 2

			if j.task.NotifyType == 0 && toEmail != "" {
				// 主题， 正文  邮件的类型

				//创建邮件的主题
				subject := fmt.Sprintf("CronJob定时任务异常：%s", j.task.TaskName)
				//创建邮件的主题内容
				body := fmt.Sprintf(
					`Hello,定时任务出问题了：
					<p style="font-size:16px;">任务执行详情：</p>
					<p style="display:block; padding:10px; background:#efefef;border:1px solid #e4e4e4">
					任务 ID：%d<br/>
					任务名称：%s<br/>
					执行时间：%s<br/>
					执行耗时：%f秒<br/>
					执行状态：%s
					</p>
					<p style="font-size:16px;">任务执行输出</p>
					<p style="display:block; padding:10px; background:#efefef;border:1px solid #e4e4e4">
					%s
					</p>
					<br/>
					<br/>
					<p>-----------------------------------------------------------------<br />
					本邮件由GoScheduleJob定时系统自动发出，请勿回复<br />
					如果要取消邮件通知，请登录到系统进行设置<br />
					</p>
					`, j.task.Id,
					j.task.TaskName,
					beego.Date(time.Unix(log.CreateTime, 0), "Y-m-d H:i:s"),
					float64(log.ProcessTime)/1000,
					TextStatus[status],
					log.Error)
				mailtype := "html" //邮件内容的类型

				ok := notify.SendToChan(toEmail, subject, body, mailtype)

				if !ok {
					fmt.Println("发送通知邮件错误")
				}

			}

		}
	}

	//写入执行日志
	id, err := models.TaskLogAdd(log)

	if err != nil {
		beego.Info(err)
	}
	beego.Info(id)

	//修改执行状态
	j.task.PrevTime = time.Now().Unix()
	j.task.ExecuteTimes++

	j.task.Update("PrevTime", "ExecuteTimes")
}

func AllAdminInfo(adminIds string) []*models.Admin {

	// 定义一个切片用来存储过滤条件
	Filters := make([]interface{}, 0)
	//过滤状态正常的管理员
	Filters = append(Filters, "status", 1)

	var notifyUserIds []int
	if adminIds != "" && adminIds != "0" {
		//通过逗号切割adminIds
		notifyUserIdStr := strings.Split(adminIds, ",")

		// 遍历切片
		for _, v := range notifyUserIdStr {
			//将字符串i 转成数字i
			id, _ := strconv.Atoi(v)
			notifyUserIds = append(notifyUserIds, id)
		}

		//追加过滤条件
		Filters = append(Filters, "id__in", notifyUserIds)
	}

	//查询
	Result, _ := models.AdminGetList(1, 100, Filters)

	return Result
}
